﻿using System;
using System.Collections.ObjectModel;
using System.Text;
using System.Threading.Tasks;
using Windows.Storage;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Animation;
using Windows.UI.Xaml.Navigation;
using Windows.UI.Xaml.Shapes;

// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238

namespace LinkLine
{
    public class Record
    {
        public string Name;
        public int Score;
    }

    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
        }

        private const string RankingFileName = "linklinerank.dat";
        private Collection<Record> records = new Collection<Record>();
        private Ellipse[,] ellipses;
        private Border[,] borders;
        private Border focusedBorder;
        private Color[] colors = { Colors.White, 
                                   Colors.Black, 
                                   Colors.Yellow, 
                                   Colors.Green, 
                                   Colors.Blue };
        private ChessCanvas chessCanvas = new ChessCanvas();
        private Random randomColor = new Random(7);
        private Random randomChess = new Random(11);
        private SolidColorBrush borderDefaultBrush = new SolidColorBrush(Colors.Black);
        private SolidColorBrush borderFocusBrush = new SolidColorBrush(Colors.YellowGreen);
        private SolidColorBrush borderBackgroundBrush = new SolidColorBrush(Colors.Gray);
        private Collection<Color> nextChessColors = new Collection<Color>();

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            if (ellipses == null)
                ellipses = new Ellipse[chessCanvas.XTotal, chessCanvas.YTotal];
            if(borders == null)
                borders = new Border[chessCanvas.XTotal, chessCanvas.YTotal];

            if (gridCanvas.ColumnDefinitions.Count != chessCanvas.XTotal)
            {
                gridCanvas.ColumnDefinitions.Clear();
                for (int i = 0; i < chessCanvas.XTotal; i++)
                    gridCanvas.ColumnDefinitions.Add(new ColumnDefinition());
            }
            if (gridCanvas.RowDefinitions.Count != chessCanvas.YTotal)
            {
                gridCanvas.RowDefinitions.Clear();
                for (int i = 0; i < chessCanvas.YTotal; i++)
                    gridCanvas.RowDefinitions.Add(new RowDefinition());
            }

            for (int i = 0; i < chessCanvas.XTotal; i++)
            {
                for (int j = 0; j < chessCanvas.YTotal; j++)
                {
                    Border border = new Border();
                    border.Background = borderBackgroundBrush;
                    border.BorderBrush = borderDefaultBrush;
                    border.BorderThickness = new Thickness(1);
                    border.PointerPressed += border_PointerPressed;
                    Grid.SetColumn(border, i);
                    Grid.SetRow(border, j);
                    gridCanvas.Children.Add(border);
                    borders[i, j] = border;

                    Ellipse ellipse = new Ellipse();
                    ellipse.Margin = new Thickness(2);
                    ellipse.PointerPressed += ellipse_PointerPressed;
                    Grid.SetColumn(ellipse, i);
                    Grid.SetRow(ellipse, j);
                    gridCanvas.Children.Add(ellipse);
                    ellipses[i, j] = ellipse;
                }
            }
            for (int i = 0; i < 9; i++)
            {
                TextBlock txtRank = new TextBlock();
                txtRank.Text = "anonymous : 0";
                txtRank.FontSize = 24;
                stackPanelRanking.Children.Add(txtRank);
            }
            GetRanking();
            Restart();
        }

        private async void GetRanking()
        {
            StorageFile rankingFile = null;
            try
            {
                rankingFile = await Windows.Storage.ApplicationData.Current.LocalFolder.GetFileAsync(RankingFileName);
            }
            catch
            {

            }
            if (rankingFile != null)
            {
                string text = await Windows.Storage.FileIO.ReadTextAsync(rankingFile);
                string[] ranks = text.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
                
                for (int i = 0; i < ranks.Length; i++)
                {
                    string[] nameAndScore = ranks[i].Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);
                    if (nameAndScore.Length == 2)
                    {
                        Record record = new Record();
                        if (int.TryParse(nameAndScore[1], out record.Score))
                        {
                            record.Name = nameAndScore[0];
                            records.Add(record);
                        }
                    }
                }
            }

            UpdateRanking(false);
        }

        private async void UpdateRanking(bool persist)
        {
            for (int i = 0; i < records.Count && i < stackPanelRanking.Children.Count; i++)
            {
                TextBlock txtRank = stackPanelRanking.Children[i] as TextBlock;
                if (txtRank != null)
                {
                    txtRank.Text = string.Format("{0} : {1}", records[i].Name, records[i].Score);
                }
            }

            if (persist)
            {
                StorageFolder storageFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
                StorageFile rankingFile = null;
                try
                {
                    rankingFile = await storageFolder.GetFileAsync(RankingFileName);
                }
                catch
                {

                }
                if (rankingFile == null)
                {
                    rankingFile = await storageFolder.CreateFileAsync(RankingFileName, CreationCollisionOption.ReplaceExisting);
                }
                StringBuilder rankingBuilder = new StringBuilder();
                foreach (Record record in records)
                {
                    rankingBuilder.AppendFormat("{0}:{1};", record.Name, record.Score);
                }
                await Windows.Storage.FileIO.WriteTextAsync(rankingFile, rankingBuilder.ToString());
            }
        }

        private async Task<bool> Select(int x, int y)
        {
            bool next = false;
            if (!chessCanvas.IsCellEmpty(x, y))
            {
                if (focusedBorder != null)
                {
                    focusedBorder.BorderBrush = borderDefaultBrush;
                }
                Border border = borders[x, y];
                border.BorderBrush = borderFocusBrush;
                focusedBorder = border;
            }
            else
            {
                if (focusedBorder != null)
                {
                    int startX = Grid.GetColumn(focusedBorder);
                    int startY = Grid.GetRow(focusedBorder);
                    Collection<ChessCanvasCell> path = chessCanvas.FindPath(startX, startY, x, y);
                    if (path.Count == 1)
                    {
                        // Do nothing.
                    }
                    else if (path.Count > 1)
                    {
                        ChessCanvasCell start = path[0];
                        for (int i = 0; i < path.Count - 1; i++)
                        {
                            ChessCanvasCell current = path[i];
                            ellipses[current.X, current.Y].Fill = new SolidColorBrush(start.Chess.Color);
                            await Task.Delay(50);
                            ellipses[current.X, current.Y].Fill = null;
                        }
                        ChessCanvasCell last = path[path.Count - 1];
                        ellipses[last.X, last.Y].Fill = new SolidColorBrush(start.Chess.Color);

                        focusedBorder.BorderBrush = borderDefaultBrush;
                        focusedBorder = null;

                        next = !chessCanvas.Set(x, y, start.Chess);
                        start.Chess = null;
                        DrawCanvas();
                    }
                    else
                    {
                        // Play sound
                    }
                }
            }
            return next;
        }

        private void DrawCanvas()
        {
            for (int i = 0; i < chessCanvas.XTotal; i++)
            {
                for (int j = 0; j < chessCanvas.YTotal; j++)
                {
                    if (!chessCanvas.IsCellEmpty(i, j))
                    {
                        SolidColorBrush brush = ellipses[i, j].Fill as SolidColorBrush;
                        if (brush == null || brush.Color != chessCanvas.Cells(i, j).Chess.Color)
                        {
                            ellipses[i, j].Fill = new SolidColorBrush(chessCanvas.Cells(i, j).Chess.Color);
                        }
                    }
                    else
                    {
                        SolidColorBrush brush = ellipses[i, j].Fill as SolidColorBrush;
                        if (brush != null)
                        {
                            ellipses[i, j].Fill = null;
                        }
                    }
                }
            }
            txtScore.Text = string.Format("Score: {0}", chessCanvas.Score);
            rectScore.Height = chessCanvas.Score;
            ellipse1.Fill = new SolidColorBrush(nextChessColors[0]);
            ellipse2.Fill = new SolidColorBrush(nextChessColors[1]);
            ellipse3.Fill = new SolidColorBrush(nextChessColors[2]);
        }

        private void GenerateNextChessColors(int newChessNumber)
        {
            nextChessColors.Clear();
            for (int i = 0; i < newChessNumber; i++)
            {
                int randomColorIndex = randomColor.Next(0, 5);
                nextChessColors.Add(colors[randomColorIndex]);
            }
        }

        private void GenerateNextChessSet(int newChessNumber)
        {
            for (int i = 0; i < newChessNumber; i++)
            {
                if (nextChessColors.Count == 0)
                {
                    GenerateNextChessColors(newChessNumber);
                }
                
                int randomChessIndex = randomChess.Next(0, chessCanvas.Total);
                int x = randomChessIndex / chessCanvas.YTotal;
                int y = randomChessIndex % chessCanvas.XTotal;
                int chessIndex = randomChessIndex;
                while (!chessCanvas.IsCellEmpty(x, y))
                {
                    if (chessIndex < chessCanvas.XTotal * chessCanvas.YTotal - 1)
                        chessIndex++;
                    else
                        chessIndex = 0;

                    if (chessIndex == randomChessIndex)
                    {
                        break;
                    }

                    x = chessIndex / chessCanvas.YTotal;
                    y = chessIndex % chessCanvas.XTotal;
                }
                Chess chess = new Chess();
                chess.Color = nextChessColors[i];
                chessCanvas.Set(x, y, chess);
            }
            GenerateNextChessColors(newChessNumber);
            DrawCanvas();
            if (chessCanvas.IsFull)
            {
                GameOver();
            }
        }

        private void GameOver()
        {
            int position = int.MaxValue;
            if (records.Count == 0)
            {
                position = 0;
            }
            else
            {
                for (int i = records.Count - 1; i >= 0; i--)
                {
                    if (chessCanvas.Score > records[i].Score)
                        position = i;
                    else
                        break;
                }
            }
            if (position <= 9)
            {
                CreateRecordPopup.IsOpen = true;
            }
            else
            {
                GameOverPopup.IsOpen = true;
            }
        }

        private void Restart()
        {
            if (focusedBorder != null)
                focusedBorder.BorderBrush = borderDefaultBrush;
            chessCanvas.Clear();
            GenerateNextChessSet(3);
        }

        private void btnStart_Click(object sender, RoutedEventArgs e)
        {
            Restart();
        }

        private void btnGameOverClose_Click(object sender, RoutedEventArgs e)
        {
            if (GameOverPopup.IsOpen)
            {
                GameOverPopup.IsOpen = false;
                Restart();
            }
        }

        private void btnCreateRecordOK_Click(object sender, RoutedEventArgs e)
        {
            if (CreateRecordPopup.IsOpen)
            {
                CreateRecordPopup.IsOpen = false;
                int position = int.MaxValue;
                if (records.Count == 0)
                {
                    position = 0;
                }
                else
                {
                    for (int i = records.Count - 1; i >= 0; i--)
                    {
                        if (chessCanvas.Score > records[i].Score)
                            position = i;
                        else
                            break;
                    }
                }
                Record record = new Record();
                record.Name = txtPlayerName.Text;
                record.Score = chessCanvas.Score;
                records.Insert(position, record);
                if (records.Count > 10)
                    records.RemoveAt(10);
                UpdateRanking(true);
                Restart();
            }
        }

        private async void ellipse_PointerPressed(object sender, PointerRoutedEventArgs e)
        {
            Ellipse ellipse = (Ellipse)sender;
            int x = Grid.GetColumn(ellipse);
            int y = Grid.GetRow(ellipse);

            bool result = await Select(x, y);
            if (result) 
                GenerateNextChessSet(3);
        }

        private async void border_PointerPressed(object sender, PointerRoutedEventArgs e)
        {
            Border border = (Border)sender;
            int x = Grid.GetColumn(border);
            int y = Grid.GetRow(border);

            bool result = await Select(x, y);
            if (result) 
                GenerateNextChessSet(3);
        }
    }
}
